/**
* Project Name:kettle-manager
* Date:2017年6月13日
* Copyright (c) 2017, jingma All Rights Reserved.
*/

package cn.benma666.kettle.ljq;

import cn.benma666.domain.SysQxYhxx;
import cn.benma666.domain.SysSjglSjdx;
import cn.benma666.domain.SysSjglSjzt;
import cn.benma666.exception.MyException;
import cn.benma666.iframe.BasicObject;
import cn.benma666.iframe.DictManager;
import cn.benma666.iframe.Result;
import cn.benma666.kettle.domain.VJob;
import cn.benma666.kettle.mytuils.Kettle;
import cn.benma666.myutils.DateUtil;
import cn.benma666.myutils.StringUtil;
import cn.benma666.sjsj.web.LjqInterface;
import cn.benma666.sjsj.web.LjqManager;
import cn.benma666.sjzt.BasicSjzt;
import cn.benma666.sjzt.Db;
import cn.benma666.sjzt.Ftp;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.Getter;
import lombok.Setter;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.plugins.PluginRegistry;
import org.pentaho.di.core.plugins.StepPluginType;
import org.pentaho.di.core.row.ValueMeta;
import org.pentaho.di.job.JobMeta;
import org.pentaho.di.job.entries.easyexpand.JobEntryEasyExpand;
import org.pentaho.di.job.entries.eval.JobEntryEval;
import org.pentaho.di.job.entries.job.JobEntryJob;
import org.pentaho.di.job.entries.shell.JobEntryShell;
import org.pentaho.di.job.entries.sql.JobEntrySQL;
import org.pentaho.di.repository.RepositoryDirectoryInterface;
import org.pentaho.di.repository.StringObjectId;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaInterface;
import org.pentaho.di.trans.steps.excelinput.ExcelInputField;
import org.pentaho.di.trans.steps.excelinput.ExcelInputMeta;
import org.pentaho.di.trans.steps.excelinput.SpreadSheetType;
import org.pentaho.di.trans.steps.excelwriter.ExcelWriterStepField;
import org.pentaho.di.trans.steps.excelwriter.ExcelWriterStepMeta;
import org.pentaho.di.trans.steps.insertupdate.InsertUpdateMeta;
import org.pentaho.di.trans.steps.tableinput.TableInputMeta;
import org.pentaho.di.trans.steps.tableoutput.TableOutputMeta;
import org.pentaho.di.trans.steps.textfileinput.TextFileInputField;
import org.pentaho.di.trans.steps.textfileinput.TextFileInputMeta;
import org.pentaho.di.trans.steps.textfileoutput.TextFileField;
import org.pentaho.di.trans.steps.textfileoutput.TextFileOutputMeta;

import java.util.*;
import java.util.Map.Entry;

/**
 * kettle服务类 <br/>
 * date: 2017年6月13日 <br/>
 * @author jingma
 * @version 0.1
 */
@Setter
@Getter
public class KettleService extends BasicObject{
    //字段常量
    /**
    * 公共作业-处理前工作
    */
    private static final String GGZY_CLQGZ = "GGZY_CLQGZ";
    /**
    * 公共作业-处理后工作
    */
    private static final String GGZY_CLHGZ = "GGZY_CLHGZ";
    /**
    * 公共作业-处理失败工作
    */
    private static final String GGZY_CLSBGZ = "GGZY_CLSBGZ";
    private static final String FIELD_ZDYS_SFGX = "是否更新";
    private static final String FIELD_ZDYS_MBZD = "目标字段";
    private static final String FIELD_ZDYS_LYZD = "来源字段";
    private static final String FIELD_GXTJ_YSF = "运算符";

    /**
    * 当前用户
    */
    private SysQxYhxx user;
    /**
    * 具体记录
    */
    private VJob job;

    //////////////////对象流转参数/////////////////////////////////
    //流转对象
    private JSONObject lydxParams;
    private JSONObject mbdxParams;
    private SysSjglSjdx lydx;
    private SysSjglSjdx mbdx;
    private String srzj;
    private String sczj;
    private String lzmb;
    //作业参数
    private JSONObject params = new JSONObject();
    //更多配置出来
    private JSONObject gdpz;
    //输入配置
    private JSONObject srpz;
    //输出配置
    private JSONObject scpz;
    private StepMeta scStep;
    private StepMeta srStep;
    private List<String> updateLookup = new ArrayList<>();
    private List<String> updateStream = new ArrayList<>();
    private List<Boolean> update = new ArrayList<>();
    private SysSjglSjzt srSjzt;
    private SysSjglSjzt scSjzt;
    //////////////////对象流转参数/////////////////////////////////

    public KettleService(JSONObject myParams) {
        super();
        user = (SysQxYhxx) myParams.get(LjqInterface.KEY_USER);
    }

    /**
    * 编辑作业基本信息 <br/>
    */
    public Result editJobInfo() throws KettleException {
        JobMeta jm;
        if(StringUtil.isNotBlank(job.getId_job())){
            jm = Kettle.use(job.getZyk()).loadJob(job.getId_job());
            jm.setJobstatus(job.getJob_status());
        }else{
            //创建作业元对象
            jm = Kettle.use(job.getZyk()).loadJobTP(job.getZylx(),"/template");
            jm.setCreatedUser(user.getYhxm());
            jm.setCreatedDate(new Date());
            jm.setObjectId(null);
            //模板的状态为草稿，此处生成的作业要设置为产品
            jm.setJobstatus(2);
        }
        //设置目录
        if(StringUtil.isNotBlank(job.getDirectory_name())){
            RepositoryDirectoryInterface dir = Kettle.use(job.getZyk())
                    .makeDirs(job.getDirectory_name());
            jm.setRepositoryDirectory(dir);
        }
        //设置作业描述
        jm.setDescription(job.getDescription());
        jm.setExtendedDescription(job.getExtended_description());
        jm.setModifiedUser(user.getYhxm());
        jm.setModifiedDate(new Date());
        jm.setJobversion(job.getJob_version());
        jm.setName(job.getName());
        return success("修改成功",jm);
    }

    /**
    * 设置作业的相对路径 <br/>
     * @return 返回作业元数据
    */
    public Result getJobInfo() throws KettleException {
        String dir = Kettle.use(job.getZyk()).getDirectory(job.getId_directory());
        JobMeta jm = Kettle.use(job.getZyk()).loadJob(job.getId_job());
        job.setDirectory_name(dir);
        job.setCreated_date(DateUtil.dateToStr14(DateUtil.longToDate(Long.parseLong(job.getCreated_date()))));
        job.setModified_date(DateUtil.dateToStr14(DateUtil.longToDate(Long.parseLong(job.getModified_date()))));
        return success("获取成功",jm);
    }

    public Result editJobJavascript() throws Exception {
        JobMeta jm = (JobMeta) editJobInfo().getData();
        JobEntryEval js = (JobEntryEval) jm.findJobEntry(job.getZylx()).getEntry();
        js.setScript(job.getJs());
        Kettle.use(job.getZyk()).saveJob(jm);
        return success("修改成功",jm);
    }
    public Result getJobJavascript() throws KettleException{
        JobMeta jm = (JobMeta) getJobInfo().getData();
        JobEntryEval js = (JobEntryEval) jm.findJobEntry(job.getZylx()).getEntry();
        job.setJs(js.getScript());
        return success("获取成功", jm);
    }

    /**
    * 编辑作业 <br/>
    * @author jingma
    * @return 作业元数据
    */
    public Result editJobShell() throws Exception {
        JobMeta jm = (JobMeta) editJobInfo().getData();
        JobEntryShell shell = (JobEntryShell) jm.findJobEntry(job.getZylx()).getEntry();
        String workPath = job.getGzlj();
        if(StringUtil.isBlank(workPath)){
            workPath = "/tmp";
        }
        shell.setWorkDirectory(workPath);
        shell.setScript(job.getShell());
        Kettle.use(job.getZyk()).saveJob(jm);
        return success("修改成功",jm);
    }

    /**
    * 作业获取 <br/>
    * @author jingma
    * @return 作业元数据
    */
    public Result getJobShell() throws KettleException{
        JobMeta jm = (JobMeta) getJobInfo().getData();
        JobEntryShell shell = (JobEntryShell) jm.findJobEntry(job.getZylx()).getEntry();
        job.setGzlj(shell.getWorkDirectory());
        job.setShell(shell.getScript());
        return success("获取成功", jm);
    }

    /**
    * 编辑作业 <br/>
    * @author jingma
    */
    public Result editJobSql() throws Exception {
        JobMeta jm = (JobMeta) editJobInfo().getData();
        String dbCode = job.getSjzt();
        DatabaseMeta dm = Kettle.createDatabaseMetaByJndi(dbCode);
        Kettle.use(job.getZyk()).saveRepositoryElement(dm);
        JobEntrySQL sql = (JobEntrySQL) jm.findJobEntry(job.getZylx()).getEntry();
        sql.setSQL(job.getSql());
        sql.setDatabase(dm);
        Kettle.use(job.getZyk()).saveJob(jm);
        return success("修改成功",jm);
    }

    /**
    * 作业获取 <br/>
    * @author jingma
    */
    public Result getJobSql() throws KettleException{
        JobMeta jm = (JobMeta) getJobInfo().getData();
        JobEntrySQL sql = (JobEntrySQL) jm.findJobEntry(job.getZylx()).getEntry();
        job.setSql(sql.getSQL());
        if(sql.getDatabase()!=null){
            job.setSjzt(sql.getDatabase().getName());
        }
        return success("获取成功", job);
    }

    /**
    * 编辑作业 <br/>
    * @author jingma
    */
    public Result editJobKm() throws Exception {
        JobMeta jm = (JobMeta) editJobInfo().getData();
        JobEntryEasyExpand km = (JobEntryEasyExpand) jm.findJobEntry(job.getZylx()).getEntry();
        km.setClassName(job.getKmlm());
        km.setConfigInfo(job.getKmpz());
        Kettle.use(job.getZyk()).saveJob(jm);
        return success("修改成功",jm);
    }

    /**
    * 作业获取 <br/>
    * @author jingma
    */
    public Result getJobKm() throws KettleException{
        JobMeta jm = (JobMeta) getJobInfo().getData();
        JobEntryEasyExpand km = (JobEntryEasyExpand) jm.findJobEntry(job.getZylx()).getEntry();
        job.setKmlm( km.getClassName());
        job.setKmpz( km.getConfigInfo());
        return success("获取成功", job);
    }

    /**
    * 编辑对象流转作业 <br/>
    */
    public Result editJobDxlz() throws Exception {
        //处理转换
        TransMeta clzh = null;
        //作业id
        int idJob = job.getId_job();
        //修改作业
        if(StringUtil.isNotBlank(idJob)){
            if(!job.getCxsc()){
                //不重新生成
                //加载转换
                clzh = Kettle.use(job.getZyk()).loadTrans("处理转换", job.getDirectory_name());
                //设置为以前的id，加载的转换好像是id丢失
                clzh.setObjectId(Kettle.use(job.getZyk()).getTransformationID(clzh));
            }else{
                //重新生成作业
                job.setId_job(null);
            }
        }
        JobMeta jm = (JobMeta) editJobInfo().getData();
        //流转模板信息
        lzmb = getLzmb().getMsg();
        JSONObject lzmbObj = DictManager.zdObjByDm("KETTLE_DXLZ_LZMB", lzmb);
        //新生成作业
        if(jm.getObjectId()==null){
            //加载模板转换
            clzh = Kettle.use(job.getZyk()).loadTransTP("处理转换-"+lzmbObj.getString("mc"),
                    "/template/对象流转");
            clzh.setRepositoryDirectory(jm.getRepositoryDirectory());
            clzh.setName("处理转换");
            if(StringUtil.isNotBlank(idJob)){
                //设置为以前的id
                jm.setObjectId(new StringObjectId(idJob+""));
                clzh.setObjectId(Kettle.use(job.getZyk()).getTransformationID("处理转换",jm.getRepositoryDirectory()));
            }else{
                clzh.setObjectId(null);
            }
        }

        //获取流转对象
        lydxParams = LjqManager.jcxxById(job.getLydx());
        mbdxParams = LjqManager.jcxxById(job.getMbdx());
        lydx = (SysSjglSjdx) lydxParams.get(LjqInterface.KEY_SJDX);
        mbdx = (SysSjglSjdx) mbdxParams.get(LjqInterface.KEY_SJDX);
        srzj = getSrzj().getMsg();
        sczj = getSczj().getMsg();
        //更多配置出来
        gdpz = JSONObject.parseObject(job.getExtended_description());
        JSONArray zdys = gdpz.getJSONArray("字段映射");
        if(zdys==null){
            zdys = new JSONArray();
        }
        String zdysStr = job.getZdys();
        if(StringUtil.isNotBlank(zdysStr)){
            for(String row:zdysStr.split("\n")){
                if(StringUtil.isBlank(row)){
                    continue;
                }
                //每行依次为：来源字段、目标字段、是否更新、字段类型、字段长度，采用制表符分隔
                String[] vals = row.split("\t");
                JSONObject ys = new JSONObject();
                ys.put(FIELD_ZDYS_LYZD, vals[0]);
                ys.put(FIELD_ZDYS_MBZD, vals[1]);
                ys.put(FIELD_ZDYS_SFGX, vals[2]);
                zdys.add(ys);
            }
        }
        gdpz.put("字段映射", zdys);
        job.setExtended_description( gdpz.toJSONString());
        gdpz.put("lydx", job.getLydx());
        gdpz.put("mbdx", job.getMbdx());
        gdpz.put("srzj", srzj);
        gdpz.put("sczj", sczj);
        gdpz.put("lzmb", lzmb);
        lydxParams.put("gdpz", gdpz);
        srpz = gdpz.getJSONObject("输入配置");
        scpz = gdpz.getJSONObject("输出配置");
        srSjzt = BasicSjzt.getSjzt(srpz.getString("数据载体"));
        scSjzt = BasicSjzt.getSjzt(scpz.getString("数据载体"));
        //作业参数处理
        for ( String key : jm.listParameters() ) {
            addParam(key, jm.getParameterDefault( key ), jm.getParameterDescription( key ));
        }
        //编辑时先清空，后续会根据需要依次添加
        setParam(GGZY_CLQGZ,"");
        setParam(GGZY_CLHGZ,"");
        setParam(GGZY_CLSBGZ,"");
        jm.eraseParameters();
        JSONArray paramArray = JSON.parseObject(lzmbObj.getString("kzxx")).getJSONArray("组件参数");
        for(JSONObject p:paramArray.toArray(new JSONObject[]{})){
            params.put(p.getString("参数代码"), p);
        }
        //加载步骤
        scStep = clzh.findStep("输出");
        srStep = clzh.findStep("输入");
        //新建流转时
        if(StringUtil.isBlank(job.getId_job())|| job.getCxsc()){
            JSONObject srzjObj = DictManager.zdObjByDm("KETTLE_DXLZ_SRZJ", srzj);
            JSONObject sczjObj = DictManager.zdObjByDm("KETTLE_DXLZ_SCZJ", sczj);
            //根据配置更换输入输出组件
            TransMeta zjjh = Kettle.use(job.getZyk()).loadTransTP("处理转换-组件集合", "/template/对象流转");
            //输入组件
            StepMetaInterface srSMI = zjjh.findStep("输入-"+srzjObj.getString("mc")).getStepMetaInterface();
            srStep.setStepMetaInterface(srSMI);
            srStep.setStepID(PluginRegistry.getInstance().getPluginId(StepPluginType.class, srSMI));
            //输出组件
            StepMetaInterface scSMI = zjjh.findStep("输出-"+sczjObj.getString("mc")).getStepMetaInterface();
            scStep.setStepMetaInterface(scSMI);
            scStep.setStepID(PluginRegistry.getInstance().getPluginId(StepPluginType.class, scSMI));
            //组件参数添加
            paramArray = JSON.parseObject(srzjObj.getString("kzxx")).getJSONArray("组件参数");
            for(JSONObject p:paramArray.toArray(new JSONObject[]{})){
                params.put(p.getString("参数代码"), p);
            }
            paramArray = JSON.parseObject(sczjObj.getString("kzxx")).getJSONArray("组件参数");
            for(JSONObject p:paramArray.toArray(new JSONObject[]{})){
                params.put(p.getString("参数代码"), p);
            }
        }

        //字段映射
        zdys = gdpz.getJSONArray("字段映射");
        for(JSONObject ys:zdys.toArray(new JSONObject[]{})){
            updateLookup.add(ys.getString(FIELD_ZDYS_MBZD).toUpperCase());
            updateStream.add(ys.getString(FIELD_ZDYS_LYZD).toUpperCase());
            update.add(ys.getBoolean(FIELD_ZDYS_SFGX));
        }
        //输入
        if(Db.isSupported(lydx.getDxztlx())){
            bzscSjksr();
        }else{
            switch (lydx.getDxztlx()) {
            case "ftp":
                srSjzt = Ftp.parseSjztFtp(srSjzt);
                addParam("XZ_FTP_IP",srSjzt.getIp(),"下载FTPIP");
                addParam("XZ_FTP_YHM",srSjzt.getYhm(),"下载FTP用户名");
                addParam("XZ_FTP_MM", srSjzt.getMm(),"下载FTP密码");
                addParam("XZ_FTP_KZBM",srSjzt.getKzxxObj().getString("$.ftppz.encodeing"),"下载FTP控制编码");
                addParam("XZ_FTP_YCML",lydx.getDxgs(),"下载FTP远程目录");
                addParam("XZ_FTP_TPF",lydx.getJtdx(),"下载FTP通配符");
                //追加ftp工作
                addParamVal(GGZY_CLQGZ,"ftp");
                setParam("SOURCE_DIR", "${FTP_LSGML}${JOB_NAME}/"+lydx.getDxgs());
                bzscBdwjsr();
                break;
            case "bdwj":
                setParam("SOURCE_DIR", srSjzt.getLjc()+lydx.getDxgs());
                bzscBdwjsr();
                break;
            default:
                throw new MyException("暂不支持该数据载体类型作为来源："+lydx.getDxztlx());
            }
        }
        //输出
        if(Db.isSupported(mbdx.getDxztlx())){
            bzscSjksc();
        }else{
            switch (mbdx.getDxztlx()) {
            case "ftp":
                scSjzt = Ftp.parseSjztFtp(scSjzt);
                addParam("SC_FTP_IP",scSjzt.getIp(),"上传FTPIP");
                addParam("SC_FTP_YHM",scSjzt.getYhm(),"上传FTP用户名");
                addParam("SC_FTP_MM",scSjzt.getMm(),"上传FTP密码");
                addParam("SC_FTP_KZBM",scSjzt.getKzxxObj().getString("$.ftppz.encodeing"),"上传FTP控制编码");
                addParam("SC_FTP_YCML",mbdx.getDxgs(),"上传FTP远程目录");
                addParam("SC_FTP_TPF",mbdx.getJtdx(),"上传FTP通配符");
                //添加工作：创建上传临时目录
                addParamVal(GGZY_CLQGZ,"cjsclsml");
                addParamVal(GGZY_CLHGZ,"ftp");
                setParam("TG_FILENAME", "${FTP_LSGML}${JOB_NAME}/${SC_FTP_YCML}"+"/"+ job.getName());
                bzscBdwjsc();
                break;
            case "bdwj":
                //作业名称作为生成文件的前缀
                String tgFilename = scSjzt.getLjc();
                if(StringUtil.isNotBlank(mbdx.getDxgs())){
                    tgFilename += (mbdx.getDxgs()+"/");
                }
                setParam("TG_FILENAME", tgFilename+ job.getName());
                bzscBdwjsc();
                break;
            default:
                throw new MyException("暂不支持该数据载体类型作为目标："+lydx.getDxztlx());
            }
        }
        //设置作业
        jm.setDescription(job.getDescription());
//        jm.setExtendedDescription(JSON.toJSONString(gdpz, true));
//        obj.put("extended_description", jm.getExtendedDescription());
        jm.setModifiedUser(user.getYhxm());
        jm.setModifiedDate(new Date());
        clzh.setModifiedUser(user.getYhxm());
        clzh.setModifiedDate(new Date());
        //设置转换
        Kettle.setParams(jm, params );
        //设置参数传递
        JobEntryJob ggzy = (JobEntryJob)jm.findJobEntry("公共作业").getEntry();
        List<String> cszList = new ArrayList<String>();
        for(String key:params.keySet()){
            cszList.add("${"+key+"}");
        }
        ggzy.parameters = params.keySet().toArray(new String[params.keySet().size()]);
        ggzy.parameterValues = cszList.toArray(new String[cszList.size()]);
        ggzy.parameterFieldNames = new String[cszList.size()];
        //保存
        Kettle.use(job.getZyk()).saveTrans(clzh);
        Kettle.use(job.getZyk()).saveJob(jm);
        return success("修改成功",jm);
    }

    /**
    * 步骤生成-本地文件输入 <br/>
    * @author jingma
    */
    private void bzscBdwjsr() {
        addParamVal(GGZY_CLHGZ,"bdwj");
        addParamVal(GGZY_CLSBGZ,"bdwj");
        setParam("SOURCE_TPF", lydx.getJtdx());
        @SuppressWarnings("unchecked")
        Map<String,JSONObject> lyFields = (Map<String, JSONObject>) lydxParams.get(LjqInterface.KEY_FIELDS);
        switch (srzj) {
        case "wbwj":
            setParam("SOURCE_FGF", srpz.getString("分隔符"));
            TextFileInputMeta text = (TextFileInputMeta) srStep.getStepMetaInterface();
            text.setEnclosure(srpz.getString("文本限定符"));
            text.setNrHeaderLines(srpz.getIntValue("头部行数"));
            text.setEncoding(srpz.getString("编码方式"));
            List<TextFileInputField> tfl = new ArrayList<TextFileInputField>();
            for(JSONObject fo:lyFields.values()){
                if("99".equals(fo.getString("zdywlb"))){
                    //跳过虚拟字段
                    continue;
                }
                TextFileInputField field = new TextFileInputField();
                field.setName(fo.getString("zddm").toUpperCase());
                field.setType(ValueMeta.getType(zdlxZH(fo.getString("zdlx"))));
                field.setLength(-1);
                field.setTrimType(ValueMeta.getTrimTypeByDesc("去掉左右两端空格"));
                tfl.add(field);
            }
            text.setInputFields(tfl.toArray(new TextFileInputField[tfl.size()]));
            break;
        case "excel":
            ExcelInputMeta excel = (ExcelInputMeta) srStep.getStepMetaInterface();
            if(lydx.getJtdx().endsWith(".xls")){
                excel.setSpreadSheetType(SpreadSheetType.JXL);
            }else if(lydx.getJtdx().endsWith(".xlsx")){
                excel.setSpreadSheetType(SpreadSheetType.POI);
            }else{
                throw new MyException("excel类型的对象的具体对象的通配符后缀必须是.xls或.xlsx");
            }
            excel.setSheetName(new String[]{srpz.getString("工作表名称")});
            excel.setStartRow(new int[]{srpz.getIntValue("起始行")});
            excel.setStartColumn(new int[]{srpz.getIntValue("起始列")});
            List<ExcelInputField> efl = new ArrayList<ExcelInputField>();
            for(JSONObject fo:lyFields.values()){
                if("99".equals(fo.getString("zdywlb"))){
                    //跳过虚拟字段
                    continue;
                }
                ExcelInputField field = new ExcelInputField();
                field.setName(fo.getString("zddm").toUpperCase());
                field.setType(ValueMeta.getType(zdlxZH(fo.getString("zdlx"))));
                //去掉两边空格
                field.setTrimType(3);
                efl.add(field);
            }
            excel.setField(efl.toArray(new ExcelInputField[efl.size()]));
            break;
        default:
            throw new MyException("不支持的输入组件："+srzj);
        }
    }

    /**
    * 步骤生成-本地文件输出 <br/>
    * @author jingma
    */
    private void bzscBdwjsc() {
        @SuppressWarnings("unchecked")
        Map<String,JSONObject> mbFields = (Map<String, JSONObject>)mbdxParams.get(LjqInterface.KEY_FIELDS);
        switch (sczj) {
        case "wbwj":
            setParam("TG_FGF", scpz.getString("分隔符"));
            TextFileOutputMeta text = (TextFileOutputMeta) scStep.getStepMetaInterface();
            text.setEnclosure(scpz.getString("文本限定符"));
            text.setEncoding(scpz.getString("编码方式"));
            List<TextFileField> tfl = new ArrayList<>();
            for(JSONObject fo:mbFields.values()){
                if("99".equals(fo.getString("zdywlb"))){
                    //跳过虚拟字段
                    continue;
                }
                TextFileField field = new TextFileField();
                field.setName(fo.getString("zddm").toUpperCase());
                field.setType(ValueMeta.getType(zdlxZH(fo.getString("zdlx"))));
                field.setLength(-1);
                field.setPrecision(-1);
                if("Number".equals(field.getTypeDesc())){
                    field.setFormat("0.#####");
                }
                field.setTrimType(ValueMeta.getTrimTypeByDesc("去掉左右两端空格"));
                tfl.add(field);
            }
            text.setOutputFields(tfl.toArray(new TextFileField[tfl.size()]));
            break;
        case "excel":
            setParam("TG_SHEET", scpz.getString("工作表名称"));
            ExcelWriterStepMeta excel = (ExcelWriterStepMeta) scStep.getStepMetaInterface();
            if(mbdx.getJtdx().endsWith(".xls")){
                excel.setExtension("xls");
            }else if(mbdx.getJtdx().endsWith(".xlsx")){
                excel.setExtension("xlsx");
            }else{
                throw new MyException("excel类型的对象的具体对象的通配符后缀必须是.xls或.xlsx");
            }
            List<ExcelWriterStepField> efl = new ArrayList<ExcelWriterStepField>();
            for(JSONObject fo:mbFields.values()){
                if("99".equals(fo.getString("zdywlb"))){
                    //跳过虚拟字段
                    continue;
                }
                ExcelWriterStepField field = new ExcelWriterStepField();
                field.setName(fo.getString("zddm").toUpperCase());
                field.setType(ValueMeta.getType(zdlxZH(fo.getString("zdlx"))));
                efl.add(field);
            }
            excel.setOutputFields(efl.toArray(new ExcelWriterStepField[efl.size()]));
            break;
        default:
            throw new MyException("不支持的输出组件："+sczj);
        }
    }

    /**
    * 步骤生成-数据库输入 <br/>
    */
    private void bzscSjksr() throws Exception{
        TableInputMeta ti = (TableInputMeta) srStep.getStepMetaInterface();
        //输入的数据库
        DatabaseMeta lydb = Kettle.createDatabaseMetaByJndi(srpz.getString("数据载体"));
        ti.setDatabaseMeta(lydb);
        setParam("SOURCE_SQL", LjqManager.getSql(lydxParams, "cqsql")[1]);
        if("zlmb".equals(gdpz.getString("lzmb"))){
            //增量模板时，处理前工作添加zl
            addParamVal(GGZY_CLQGZ,"zl");
        }
    }

    /**
    * 步骤生成-数据库输出 <br/>
    * @author jingma
    */
    private void bzscSjksc() {
        setParam("TG_SCHEMA",mbdx.getDxgs());
        setParam("TG_TABLE",mbdx.getJtdx());
        //输出的数据库
        String pltjl = scpz.getString("输出批量提交量");
        DatabaseMeta mbdb = Kettle.createDatabaseMetaByJndi(scpz.getString("数据载体"));
        switch (sczj) {
        case "crgx":
            InsertUpdateMeta iu = (InsertUpdateMeta) scStep.getStepMetaInterface();
            iu.setCommitSize(pltjl);
            iu.setUpdateBypassed(scpz.getBoolean("输出不执行更新"));
            iu.setDatabaseMeta(mbdb);
            //设置字段映射
            iu.setUpdateLookup(updateLookup.toArray(new String[]{}));
            iu.setUpdateStream(updateStream.toArray(new String[]{}));
            iu.setUpdate(update.toArray(new Boolean[]{}));
            //设置去重条件
            List<String> keyLookup = new ArrayList<String>();
            List<String> keyStream = new ArrayList<String>();
            List<String> keyCondition = new ArrayList<String>();
            JSONObject[] gxtjs = gdpz.getJSONArray("更新条件").toArray(new JSONObject[]{});
            if(gxtjs.length==0){
                throw new MyException("插入更新模式必须设置更新条件");
            }
            for(JSONObject ys:gxtjs){
                keyLookup.add(ys.getString(FIELD_ZDYS_MBZD).toUpperCase());
                keyStream.add(ys.getString(FIELD_ZDYS_LYZD).toUpperCase());
                keyCondition.add(ys.getString(FIELD_GXTJ_YSF));
            }
            iu.setKeyLookup(keyLookup.toArray(new String[]{}));
            iu.setKeyStream(keyStream.toArray(new String[]{}));
            iu.setKeyStream2(new String[keyStream.size()]);
            iu.setKeyCondition(keyCondition.toArray(new String[]{}));
            break;
        case "bsc":
            TableOutputMeta to = (TableOutputMeta) scStep.getStepMetaInterface();
            to.setCommitSize(pltjl);
            to.setDatabaseMeta(mbdb);
            to.setTruncateTable(scpz.getBooleanValue("裁剪表"));
            //设置字段映射
            to.setFieldDatabase(updateLookup.toArray(new String[]{}));
            to.setFieldStream(updateStream.toArray(new String[]{}));
            break;
        default:
            throw new MyException("不支持的输出模式："+sczj);
        }
    }

    /**
    * 追加参数值 <br/>
    * @author jingma
    * @param csdm
    * @param mrz
    */
    private void addParamVal(String csdm, String mrz) {
        JSONObject p = params.getJSONObject(csdm);
        p.put("默认值", p.getString("默认值")+mrz+",");
    }
    /**
    * 设置参数 <br/>
    * @author jingma
    * @param csdm
    * @param mrz
    */
    private void setParam(String csdm, String mrz) {
        JSONObject p = params.getJSONObject(csdm);
        if(p==null){
            p = new JSONObject();
            params.put(csdm, p);
        }
        p.put("默认值", mrz);
    }

    /**
    * 添加作业参数 <br/>
    * @author jingma
    * @param csdm
    * @param mrz
    * @param csmc
    */
    private void addParam(String csdm, String mrz, String csmc) {
        JSONObject p = new JSONObject();
        p.put("参数代码", csdm);
        p.put("参数名称", csmc);
        p.put("默认值", mrz);
        params.put(csdm, p);
    }

    /**
    * 获取流转模板 <br/>
    */
    private Result getLzmb() {
        String lzmb = job.getLzmb();
        if(StringUtil.isNotBlank(lzmb)){
            return success(lzmb);
        }
        if(Db.isSupported(lydx.getDxztlx())){
            lzmb = "zlmb";
        }else{
            switch (lydx.getDxztlx()) {
            case "ftp":
            case "bdwj":
                lzmb = "mrmb";
                break;
            default:
                throw new MyException("暂不支持该数据载体类型作为来源："+lydx.getDxztlx());
            }
        }
        return success(lzmb);
    }

    /**
    * 获取输入组件 <br/>
    */
    private Result getSrzj() {
        String srzj = job.getSrzj();
        if(StringUtil.isNotBlank(srzj)){
            return success(srzj);
        }
        JSONObject lzpz = JSON.parseObject(DictManager.zdObjByDm("SYS_SJGL_DXLX", lydx.getDxlx()).
                getString("kzxx")).getJSONObject("流转配置");
        if(lzpz==null||lzpz.getString("输入组件")==null){
            throw new MyException("该对象类型没有配置默认输入组件，请在字典管理中配置："+lydx.getDxlx());
        }
        srzj = lzpz.getString("输入组件");
        return success(srzj);
    }

    /**
    * 获取输出组件 <br/>
    */
    private Result getSczj() {
        String sczj = job.getSczj();
        if(StringUtil.isNotBlank(sczj)){
            return success(sczj);
        }
        JSONObject lzpz = JSON.parseObject(DictManager.zdObjByDm("SYS_SJGL_DXLX", mbdx.getDxlx()).
                getString("kzxx")).getJSONObject("流转配置");
        if(lzpz==null||lzpz.getString("输出组件")==null){
            throw new MyException("该对象类型没有配置默认输出组件，请在字典管理中配置："+mbdx.getDxlx());
        }
        sczj = lzpz.getString("输出组件");
        return success(sczj);
    }

    /**
    * 获取对象流转默认配置 <br/>
    */
    public Result getDxlzMrpz() {
        //获取基本信息
        lydxParams = LjqManager.jcxxById(job.getLydx());
        mbdxParams = LjqManager.jcxxById(job.getMbdx());
        lydx = (SysSjglSjdx) lydxParams.get(LjqInterface.KEY_SJDX);
        mbdx = (SysSjglSjdx) mbdxParams.get(LjqInterface.KEY_SJDX);
        srzj = getSrzj().getMsg();
        sczj = getSczj().getMsg();
        lzmb = getLzmb().getMsg();
        //创建更多配置对象
        gdpz = new JSONObject();
        //处理字段映射
        getMrzdys();
        //来源对象处理
        gdpz.put("输入配置", lydxMrpz());
        //目标对象处理
        gdpz.put("输出配置", mbdxMrpz());

        JSONObject r = new JSONObject();
        r.put("lzmb", lzmb);
        r.put("sczj", sczj);
        r.put("srzj", srzj);
        JSONArray zdys = gdpz.getJSONArray("字段映射");
        if(zdys==null){
            zdys = new JSONArray();
        }
        StringBuffer zdysStr = new StringBuffer();
        for(JSONObject row:zdys.toArray(new JSONObject[]{})){
            zdysStr.append(row.getString(FIELD_ZDYS_LYZD)+"\t");
            zdysStr.append(row.getString(FIELD_ZDYS_MBZD)+"\t");
            zdysStr.append(row.getString(FIELD_ZDYS_SFGX)+"\n");
        }
        gdpz.remove("字段映射");
        r.put("extended_description",gdpz.toJSONString());
        r.put("zdys", zdysStr.toString());
        return success("获取配置成功", r);
    }

    /**
    * 目标对象默认配置 <br/>
    */
    private JSONObject mbdxMrpz() {
        JSONObject scpz = new JSONObject();
        scpz.put("数据载体", mbdx.getDxzt());
        //获取目标对象基础配置
        scpz.putAll(mbdxParams.getJSONObject("$.sjdx.kzxx['基础配置']"));
        //获取组件默认配置
        JSONObject sczjObj = DictManager.zdObjByDm("KETTLE_DXLZ_SCZJ", sczj);
        scpz.putAll(getZjpz(sczjObj));
        return scpz;
    }

    /**
    * 来源对象默认配置 <br/>
    */
    private JSONObject lydxMrpz() {
        JSONObject srpz = new JSONObject();
        srpz.put("数据载体", lydx.getDxzt());
        //获取目标对象基础配置
        srpz.putAll(lydxParams.getJSONObject("$.sjdx.kzxx['基础配置']"));
        //获取组件默认配置
        JSONObject srzjObj = DictManager.zdObjByDm("KETTLE_DXLZ_SRZJ", srzj);
        srpz.putAll(getZjpz(srzjObj));
        return srpz;
    }

    /**
    * 获取组件配置 <br/>
    * @author jingma
    * @param zjObj
    * @return
    */
    private JSONObject getZjpz(JSONObject zjObj) {
        JSONObject zjpz = zjObj.getJSONObject("$.kzxx.组件配置");
        if(zjpz==null){
            zjpz = new JSONObject();
        }
        return zjpz;
    }

    /**
    * 获取默认字段映射 <br/>
    */
    @SuppressWarnings("unchecked")
    private void getMrzdys() {
        JSONArray zdys = new JSONArray();
        JSONArray gxtj = new JSONArray();
        Map<String,JSONObject> lyFields = (Map<String, JSONObject>) lydxParams.get(LjqInterface.KEY_FIELDS);
        Map<String,JSONObject> mbFields = (Map<String, JSONObject>) mbdxParams.get(LjqInterface.KEY_FIELDS);
        //来源字段基于id的Map
        Map<String,JSONObject> lyFieldsByid = new HashMap<String,JSONObject>();
        //来源字段基于标准字段的Map
        Map<String,JSONObject> lyFieldsByBzzd = new HashMap<String,JSONObject>();
        for(JSONObject ly:lyFields.values()){
            lyFieldsByid.put(ly.getString("id"),ly);
            lyFieldsByBzzd.put(ly.getString("bzzd"),ly);
        }
        for(Entry<String, JSONObject> mbe:mbFields.entrySet()){
            JSONObject mbf = mbe.getValue();
            if("99".equals(mbf.getString("zdywlb"))){
                //跳过虚拟字段
                continue;
            }
            if(mbf.getString("zddm").equals(mbdx.getZlzd())&&(
                    "bsr".equals(sczj)||"crgx".equals(sczj))){
                //该字段为增量字段，插入数据需要保持递增
                JSONObject f = new JSONObject();
                //递增的时间在流程中通过js生成
                f.put(FIELD_ZDYS_LYZD, "my_zlsj");
                f.put(FIELD_ZDYS_MBZD, mbf.getString("zddm"));
                f.put(FIELD_ZDYS_SFGX, true);
                zdys.add(f);
                continue;
            }
            //字段代码相同
            JSONObject lyf = lyFields.get(mbe.getKey());
            if(lyf==null){
                //目标字段的标准字段在来源对象中
                lyf = lyFieldsByid.get(mbf.getString("bzzd"));
            }
            if(lyf==null){
                //目标字段是来源对象字段的标准字段
                lyf = lyFieldsByBzzd.get(mbf.getString("id"));
            }
            if(lyf!=null){
                JSONObject f = new JSONObject();
                f.put(FIELD_ZDYS_LYZD, lyf.getString("zddm"));
                f.put(FIELD_ZDYS_MBZD, mbf.getString("zddm"));
                f.put(FIELD_ZDYS_SFGX, mbf.getBoolean("yxbj"));
                zdys.add(f);
                //更新条件判断
                if(mbf.getIntValue("qcbh")>0){
                    //去重编号大于0表示为去重字段
                    //更新条件
                    JSONObject tj = new JSONObject();
                    tj.put(FIELD_ZDYS_LYZD, lyf.getString("zddm"));
                    tj.put(FIELD_ZDYS_MBZD, mbf.getString("zddm"));
                    tj.put(FIELD_GXTJ_YSF, "=");
                    gxtj.add(tj);
                }
            }
        }
        if(gxtj.size()==0){
            //当没有单独配置去重字段时，采用主键去重
            JSONObject tj = new JSONObject();
            tj.put(FIELD_ZDYS_LYZD, lydx.getZjzd());
            tj.put(FIELD_ZDYS_MBZD, mbdx.getZjzd());
            tj.put(FIELD_GXTJ_YSF, "=");
            gxtj.add(tj);
        }
        gdpz.put("字段映射", zdys);
        gdpz.put("更新条件", gxtj);
    }

    /**
    * 数据世界字段类型转为kettle数据类型 <br/>
    * @author jingma
    * @param zdlx
    * @return
    */
    private String zdlxZH(String zdlx) {
        String nzdlx = DictManager.zdMcByDm("KETTLE_DXLZ_ZDLXYS", zdlx);
        if(zdlx.equals(nzdlx)){
            //没有配置映射关系
            nzdlx = "String";
        }
        return nzdlx;
    }

    public Result getJobDxlz() throws KettleException{
        getJobInfo();
        gdpz = JSONObject.parseObject(job.getExtended_description());
        JSONArray zdys = gdpz.getJSONArray("字段映射");
        if(zdys==null){
            zdys = new JSONArray();
        }
        StringBuffer zdysStr = new StringBuffer();
        for(JSONObject row:zdys.toArray(new JSONObject[]{})){
            zdysStr.append(row.getString(FIELD_ZDYS_LYZD)).append("\t");
            zdysStr.append(row.getString(FIELD_ZDYS_MBZD)).append("\t");
            zdysStr.append(row.getString(FIELD_ZDYS_SFGX)).append("\n");
        }
        gdpz.remove("字段映射");
        job.setExtended_description(gdpz.toJSONString());
        job.setZdys(zdysStr.toString());
        return success("获取成功", job);
    }

    /**
    * 导入作业 <br/>
    */
    public Result impJob() {
        return failed("暂不支持");
    }

    /**
    * 导入转换 <br/>
    */
    public static Result impTrans() {
        return failed("暂不支持");
    }

}
